// scaup - Scala up!
// (c) 2008-2010, Normen Müller, normen.mueller@gmail.com
// Original version by Jeremy Cloud (http://github.com/jjmmcc/scala0.cmdline#)
// $Id: CommandLine.scala 68 2010-04-25 10:20:41Z normen.mueller $
package scaup.draft.cmdln

object CommandLine {
  val empty = new CommandLine(Map.empty)

  /** Builds a CommandLine object.  Will thrown an error if the type of a value is not applicable for the corresponding Argument object.
   * For example, the value for an IntOption must be an Int, the value for a StringOption must be a String, the value for a Flag must be true,
   * the value for a NakedArgument must a String or List of Strings. */
   def apply(args: (Argument,Any)*) = {
     var res: CommandLine = empty
     args foreach {
       case (flag: Flag, true)                              => res += flag                
       case (strOpt: StringOption, value: String)           => res += (strOpt, value)                
       case (intOpt: IntOption, value: Int)                 => res += (intOpt, value)                
       case (nakedArg: NakedArgument, value: String)        => res += (nakedArg, List(value))
       //case (nakedArg: NakedArgument, values: List[String]) => res += (nakedArg, values)
       case (nakedArg: NakedArgument, values: List[_]) => res += (nakedArg, values.asInstanceOf[List[String]])
     }
     res
   }
}

/** This class encapsulates the values captured from the command line, and is returned by the CommandLineParser.apply method. */
final class CommandLine private(private val map: Map[Argument,Any]) {
  /** Returns the number of options/arguments in the command line.  An option name and value count as 1, as do a repeated naked argument. */
  def size = map.size
    
  /** Returns true if the command line contains the given option, false otherwise. */
  def contains(arg: Argument) = map.contains(arg)

  /** Returns true if the flag was provided, false otherwise. */
  def apply(flag: Flag) = map.contains(flag)
    
  /** Returns the string if provided, None otherwise. */ 
  def apply(opt: StringOption) = map.get(opt) match {
    case Some(str: String) => Some(str)
    case _ => None
  }
    
  /** Returns the int if provided, None otherwise. */ 
  def apply(opt: IntOption) = map.get(opt) match {
    case Some(i: Int) => Some(i)
    case _ => None
  }
    
  /** Returns any tail arguments */
  def apply(opt: NakedArgument) = map.get(opt) match {
    //@todo how to get rid of this unchecked warning?
    case Some(values: List[_]) => values.asInstanceOf[List[String]]
    case _ => Nil
  }
    
  /** Returns a new CommandLine with the given flag added to the set of options. */
  def +(flag: Flag): CommandLine = new CommandLine(map + (flag -> true))
    
  /** Returns a new CommandLine with the given option added in */
  def +(strOpt: StringOption, value: String): CommandLine = new CommandLine(map + (strOpt -> value))
    
  /** Returns a new CommandLine with the given option added in */
  def +(intOpt: IntOption, value: Int): CommandLine = new CommandLine(map + (intOpt -> value))
    
  /** Returns a new CommandLine with the given arguments added in */
  def +(nakedArg: NakedArgument, values: List[String]): CommandLine = new CommandLine(map + (nakedArg -> values))
    
  override def equals(a: Any) = a match {
    case that: CommandLine => this.map == that.map
  }
    
  override def toString = map map {
    case (flag: Flag, true) => flag.toString
    case (strOpt: StringOption, value: String) => strOpt.toString(value)
    case (intOpt: IntOption, value: Int) => intOpt.toString(value.toString)
    //case (nakedArg: NakedArgument, values: List[String]) => values.mkString(" ")
    case (nakedArg: NakedArgument, values: List[_]) => values.mkString(" ")
  } mkString(" ")
}
